/**
 * @file libgrocket/gr_server_impl.c
 * @author zouyueming(da_ming at hotmail.com)
 * @date 2013/10/05
 * @version $Revision$ 
 * @brief   server framework main function
 * Revision History
 *
 * @if  ID       Author       Date          Major Change       @endif
 *  ---------+------------+------------+------------------------------+
 *       1     zouyueming   2013-10-05    Created.
 **/
/* 
 *
 * Copyright (C) 2013-now da_ming at hotmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "gr_server_impl.h"
#include "gr_log.h"
#include "gr_global.h"
#include "gr_errno.h"
#include "gr_tools.h"
#include "gr_config.h"
#include "gr_mem.h"
#include "gr_tcp_accept.h"
#include "gr_tcp_close.h"
#include "gr_tcp_in.h"
#include "gr_udp_in.h"
#include "gr_tcp_out.h"
#include "gr_udp_out.h"
#include "gr_worker.h"
#include "gr_http.h"
#include "gr_conn.h"
#include "gr_backend.h"
#include "gr_module.h"
#include "gr_socket.h"
#include "gr_http.h"
#include <signal.h>

int gr_server_init()
{
    gr_server_impl_t *  server;

    if ( NULL != g_ghost_rocket_global.server ) {
        gr_fatal( "[init]gr_server_init_config already called" );
        return GR_ERR_WRONG_CALL_ORDER;
    }

    server = (gr_server_impl_t *)gr_calloc( 1, sizeof( gr_server_impl_t ) );
    if ( NULL == server ) {
        gr_fatal( "[init]malloc %d bytes failed, errno=%d,%s",
            (int)sizeof(gr_server_impl_t), errno, strerror( errno ) );
        return GR_ERR_BAD_ALLOC;
    }

    g_ghost_rocket_global.server = server;
    return GR_OK;
}

void gr_server_term()
{
    if ( NULL != g_ghost_rocket_global.server ) {
        gr_free( g_ghost_rocket_global.server );
        g_ghost_rocket_global.server = NULL;
    }
}

void gr_server_need_exit( gr_server_impl_t * server )
{
    g_ghost_rocket_global.server_interface.is_server_stopping = true;
}

void do_close()
{
    // 做退出动作, 如果主程序在deamon=1时调用此功能,则服务器会重启
    gr_server_impl_t * server = g_ghost_rocket_global.server;
    if ( NULL == server ) {
        gr_error( "global.server is NULL" );
        return;
    }

    gr_server_need_exit( server );
}

#if defined( WIN32 ) || defined( WIN64 )

static
BOOL WINAPI
process_signal(
    DWORD ctrl_type
)
{
    switch( ctrl_type )
    {
    case CTRL_BREAK_EVENT:      // A CTRL+C or CTRL+BREAK signal was
                                // received, either from keyboard input
                                // or from a signal generated by
                                // GenerateConsoleCtrlEvent.

    case CTRL_C_EVENT:          // SERVICE_CONTROL_STOP in debug mode or
                                // A CTRL+c signal was received, either
                                // from keyboard input or from a signal
                                // generated by the GenerateConsoleCtrlEvent
                                // function.

    case CTRL_CLOSE_EVENT:      // A signal that the system sends to all
                                // processes attached to a console when
                                // the user closes the console (either
                                // by choosing the Close command from
                                // the console window's System menu, or
                                // by choosing the End Task command from
                                // the Task List).

    case CTRL_SHUTDOWN_EVENT:   // A signal that the system sends to all
                                // console processes when the system is
                                // shutting down.
        printf( "!!!!!! stopping signal %d !!!!!!\n", (int)ctrl_type );
        gr_info( "[signal][sig=%d]stopping signal", (int)ctrl_type );
        do_close();

        return TRUE;

    default:
        printf( "!!!!!! receive unknown signal %d !!!!!!\n", (int)ctrl_type );
        gr_error( "receive unknown signal %d", (int)ctrl_type );
        return FALSE;
    }
}

#else

static
void
process_signal(
    int sig
)
{
    signal( sig, process_signal );
    printf( "!!!!!! stopping signal %d !!!!!!\n", sig );
    gr_info( "[signal][sig=%d]stopping signal", sig );
    do_close();
}

#endif  // signal

static_inline
void init_signal()
{
#if defined( WIN32 ) || defined( WIN64 )
    SetConsoleCtrlHandler( process_signal, TRUE );
#else
    // SIGHUP 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控 
    // 制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端 
    // 不再关联. 
    signal( SIGHUP, SIG_IGN );

    // SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
    signal( SIGINT, process_signal );

    // SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到 
    // SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信 
    // 号.
    signal( SIGQUIT, process_signal );

    // SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行 
    // 数据段. 堆栈溢出时也有可能产生这个信号. 

    // SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用. 

    // SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和 
    // 处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这 
    // 个信号. 
    signal( SIGTERM, process_signal );

    // SIGIOT 在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样. 
    signal( SIGIOT, process_signal );

    // SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长 
    // 的整数, 但其地址不是4的倍数. 

    // SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢 
    // 出及除数为0等其它所有的算术的错误. 

    // SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略. 

    // SIGPIPE Broken pipe 
    signal( SIGPIPE, SIG_IGN );

    // SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时 
    // (通常是Ctrl-Z)发出这个信号 
    signal( SIGTSTP, process_signal );

    // SIGCHLD 子进程结束时, 父进程会收到这个信号. 
    signal( SIGCHLD, SIG_IGN );

    // SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/ 
    // 改变
    signal( SIGXCPU, SIG_IGN );

    // SIGXFSZ 超过文件大小资源限制
    signal( SIGXFSZ, SIG_IGN );
#endif
}

static_inline
bool has_tcp()
{
    gr_server_t * server_interface = & g_ghost_rocket_global.server_interface;
    int i;
    for ( i = 0; i < server_interface->ports_count; ++ i ) {
        gr_port_item_t * item = & server_interface->ports[ i ];
        if ( item->is_tcp ) {
            return true;
        }
    }
    return false;
}

static_inline
bool has_udp()
{
    gr_server_t * server_interface = & g_ghost_rocket_global.server_interface;
    int i;
    for ( i = 0; i < server_interface->ports_count; ++ i ) {
        gr_port_item_t * item = & server_interface->ports[ i ];
        if ( ! item->is_tcp ) {
            return true;
        }
    }
    return false;
}

static_inline
int
server_init(
    gr_server_impl_t * server
)
{
    int     r       = GR_OK;

#if defined( WIN32 ) || defined( WIN64 )
    gr_info( "Windows not allow to add a socket fd to difference IOCP, so I fuck this!" );
#endif

    do {

        if ( has_tcp() ) {

            // 初始化TCP accept模块
            r = gr_tcp_accept_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_tcp_accept_init() return error %d", r );
                r = GR_ERR_INIT_TCP_ACCEPT_FALED;
                break;
            }

            // 初始化TCP close模块
            r = gr_tcp_close_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_tcp_close_init() return error %d", r );
                r = GR_ERR_INIT_TCP_CLOSE_FALED;
                break;
            }

            // 初始化TCP读模块
            r = gr_tcp_in_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_tcp_in_init() return error %d", r );
                r = GR_ERR_INIT_TCP_IN_FALED;
                break;
            }

            // 初始化TCP写模块
            r = gr_tcp_out_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_tcp_out_init() return error %d", r );
                r = GR_ERR_INIT_TCP_OUT_FALED;
                break;
            }
        }

        if ( has_udp() ) {
            // 初始化UDP读模块
            r = gr_udp_in_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_udp_in_init() return error %d", r );
                r = GR_ERR_INIT_UDP_IN_FALED;
                break;
            }

            // 初始化UDP写模块
            r = gr_udp_out_init();
            if ( 0 != r ) {
                gr_fatal( "[init]gr_udp_out_init() return error %d", r );
                r = GR_ERR_INIT_UDP_OUT_FALED;
                break;
            }
        }

        // 初始化http模块
        r = gr_http_init();
        if ( 0 != r ) {
            gr_fatal( "[init]gr_http_init() return error %d", r );
            r = GR_ERR_INIT_HTTP_FALED;
            break;
        }

        // 初始化conn模块
        r = gr_conn_init();
        if ( 0 != r ) {
            gr_fatal( "[init]gr_conn_init() return error %d", r );
            r = GR_ERR_INIT_CONN_FALED;
            break;
        }

        // 初始化backend模块
        r = gr_backend_init();
        if ( 0 != r ) {
            gr_fatal( "[init]gr_backend_init() return error %d", r );
            r = GR_ERR_INIT_CONN_FALED;
            break;
        }

        // 初始化worker模块
        r = gr_worker_init();
        if ( 0 != r ) {
            gr_fatal( "[init]gr_worker_init() return error %d", r );
            r = GR_ERR_INIT_WORKER_FALED;
            break;
        }

    } while ( false );

    return r;
}

static_inline
void
server_term(
    gr_server_impl_t * server
)
{
    // 卸载worker模块
    gr_worker_term();
    // 卸载backend模块
    gr_backend_term();
    // 卸载conn模块
    gr_conn_term();
    // 卸载http模块
    gr_http_term();

    if ( has_udp() ) {
        // 卸载UDP写模块
        gr_udp_out_term();
        // 卸载UDP读模块
        gr_udp_in_term();
    }

    if ( has_tcp() ) {
        // 卸载TCPP写模块
        gr_tcp_out_term();
        // 卸载TCP读模块
        gr_tcp_in_term();
        // 卸载tcp close模块
        gr_tcp_close_term();
        // 卸载tcp accept模块
        gr_tcp_accept_term();
    }
}

static
int
start_listen(
    gr_server_impl_t * server
)
{
    gr_server_t * server_interface = & g_ghost_rocket_global.server_interface;
    int i;
    int r = 0;
    int listen_backlog = gr_config_get_listen_backlog();

    if ( has_tcp() ) {
        for ( i = 0; i < server_interface->ports_count; ++ i ) {
            gr_port_item_t * item = & server_interface->ports[ i ];

            if ( item->is_tcp ) {
                // listen
                if ( -1 == listen( item->fd, listen_backlog ) ) {
                    gr_error( "[init]listen for port %d failed: %d", item->port, get_errno() );
                    r = -1;
                    break;
                }
            }
        }

        r = gr_tcp_accept_add_listen_ports();
        if ( 0 != r ) {
            gr_error( "[init]gr_tcp_accept_add_listen_ports() return %d", r );
            r = -2;
        }
    }

    if ( has_udp() ) {
        r = gr_udp_in_add_listen_ports();
        if ( 0 != r ) {
            gr_error( "[init]gr_udp_in_add_listen_ports() return %d", r );
            r = -3;
        }
    }

    return 0;
}

static_inline
int
server_loop(
    gr_server_impl_t * server
)
{
    int r = start_listen( server );
    if ( 0 != r ) {
        gr_error( "[init]start_listen() return %d", -1 );
        return r;
    }

    while ( ! g_ghost_rocket_global.server_interface.is_server_stopping ) {
        sleep_ms( 100 );
    }

    return 0;
}

static_inline
int
bind_tcp(
    gr_server_impl_t * server,
    gr_port_item_t * item
)
{
    int r = 0;
    item->fd = (int)socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( -1 == item->fd ) {
        gr_error( "[init]socket invalid, errno = %d", get_errno() );
        return -1;
    }

    do {
        int reuse_addr = 1;

        // 发送缓冲区
        if ( ! gr_socket_set_send_buf( item->fd, gr_config_tcp_accept_send_buf() ) ) {
            gr_warning( "[init]gr_socket_set_send_buf failed, ignore" );
        }

        // 接收缓冲区
        if ( ! gr_socket_set_recv_buf( item->fd, gr_config_tcp_accept_recv_buf() ) ) {
            gr_warning( "[init]gr_socket_set_recv_buf failed, ignore" );
        }

        // 非阻塞
        if ( ! gr_socket_set_block( item->fd, false ) ) {
            r = -5;
            break;
        }

        // 重用地址
        setsockopt( item->fd, SOL_SOCKET, SO_REUSEADDR, (const char*)& reuse_addr, sizeof( reuse_addr ) );

        // bind
        if ( -1 == bind( item->fd, (struct sockaddr*)& item->addr, item->addr_len ) ) {
            gr_error( "[init]bind tcp port %d failed: %d", item->port, get_errno() );
            r = -6;
            break;
        }

        r = 0;
    } while( 0 );

    if ( 0 != r ) {
        gr_error( "[init]bind_tcp failed" );
        gr_socket_close( item->fd );
        item->fd = -1;
        return r;
    }

    return 0;
}

static_inline
int
bind_udp(
    gr_server_impl_t * server,
    gr_port_item_t * item
)
{
    int r = 0;
    item->fd = (int)socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
    if ( -1 == item->fd ) {
        gr_error( "[init]socket invalid, errno = %d", get_errno() );
        return -1;
    }

    do {
        int reuse_addr = 1;

        // 发送缓冲区
        if ( ! gr_socket_set_send_buf( item->fd, gr_config_udp_send_buf() ) ) {
            r = -3;
            break;
        }

        // 接收缓冲区
        if ( ! gr_socket_set_recv_buf( item->fd, gr_config_udp_recv_buf() ) ) {
            r = -4;
            break;
        }

        // 非阻塞
        if ( ! gr_socket_set_block( item->fd, false ) ) {
            r = -5;
            break;
        }

        // 重用地址
        setsockopt( item->fd, SOL_SOCKET, SO_REUSEADDR, (const char*)& reuse_addr, sizeof( reuse_addr ) );

        // bind
        if ( -1 == bind( item->fd, (struct sockaddr*)& item->addr, item->addr_len ) ) {
            gr_error( "[init]bind udp port %d failed: %d", item->port, get_errno() );
            r = -6;
            break;
        }

        r = 0;
    } while( 0 );

    if ( 0 != r ) {
        gr_error( "[init]bind_udp failed" );
        gr_socket_close( item->fd );
        item->fd = -1;
        return r;
    }

    return 0;
}

static
int
server_bind_port(
    gr_server_impl_t * server
)
{
    gr_server_t * server_interface = & g_ghost_rocket_global.server_interface;
    int i;
    int r;

    for ( i = 0; i < server_interface->ports_count; ++ i ) {
        gr_port_item_t * item = & server_interface->ports[ i ];

        if ( item->is_tcp ) {
            r = bind_tcp( server, item );
            if ( 0 != r ) {
                gr_error( "[init]listen_tcp return %d", r );
                return -1;
            }
        } else {
            r = bind_udp( server, item );
            if ( 0 != r ) {
                gr_error( "[init]listen_udp return %d", r );
                return -2;
            }
        }
    }

    return 0;
}

static_inline
int
server_run(
    gr_server_impl_t * server
)
{
    int r = GR_OK;
    bool child_process_init_ok = false;
    bool server_init_called = false;

    do {

        // 把端口绑了
        r = server_bind_port( server );
        if ( 0 != r ) {
            gr_fatal( "[init]server_bind_port return error %d", r );
            break;
        }

        // 调用模块子进程初始化函数
        r = gr_module_child_process_init();
        if ( 0 != r ) {
            gr_fatal( "[init]gr_module_child_process_init return error %d", r );
            break;
        }
        child_process_init_ok = true;

        // 初始化服务器，这其中包括每个worker初始化的函数
        server_init_called = true;
        r = server_init( server );
        if ( 0 != r ) {
            gr_fatal( "[init]server_init return error %d", r );
            break;
        }

        // 服务器主循环
        r = server_loop( server );
        gr_info( "[term]server_loop return %d", r );

    } while ( false );

    if ( server_init_called ) {
        // 反初始化服务器，这其中包括每个worker反初始化的函数
        server_term( server );
    }

    // 调用模块了进程反初始化函数
    if ( child_process_init_ok ) {
        gr_module_child_process_term();
    }

    return r;
}

int gr_server_main()
{
    // 注意：这个函数是子进程调用的代码
    int r;
    time_t start;
    time_t stop;
    gr_server_impl_t *  server;

    if ( NULL == g_ghost_rocket_global.server ) {
        gr_error( "[init]global.server is NULL" );
        return GR_ERR_INVALID_PARAMS;
    }
    server  = g_ghost_rocket_global.server;

    init_signal();

    gr_info( "[init]GRocket Server Started" );

    time( & start );
    r = server_run( server );
    time( & stop );

    gr_info( "[term]GRocket Server will exit(%d), running %d seconds(%d minutes | %d hours)",
        r,
        (int)(stop - start),
        (int)(stop - start) / 60,
        (int)(stop - start) / 60 / 60
    );

    return r;
}
